package com.hqd.ch03.v28.tx.transaction.support;

import com.hqd.ch03.v28.core.Constants;
import com.hqd.ch03.v28.tx.transaction.PlatformTransactionManager;
import com.hqd.ch03.v28.tx.transaction.TransactionDefinition;
import com.hqd.ch03.v28.tx.transaction.TransactionStatus;
import lombok.Data;

import java.util.List;

import static com.hqd.ch03.v28.tx.transaction.support.TransactionSynchronizationUtils.*;

@Data
public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager {
    /**
     * 一直触发事务同步器
     */
    public static final int SYNCHRONIZATION_ALWAYS = 0;
    /**
     * 存在活跃事务时候触发
     */

    public static final int SYNCHRONIZATION_ON_ACTUAL_TRANSACTION = 1;

    /**
     * 一直都不触发
     */
    public static final int SYNCHRONIZATION_NEVER = 2;

    private static final Constants constants = new Constants(AbstractPlatformTransactionManager.class);

    private int transactionSynchronization = SYNCHRONIZATION_ALWAYS;

    private int defaultTimeout = TransactionDefinition.TIMEOUT_DEFAULT;

    private boolean nestedTransactionAllowed = false;

    private boolean validateExistingTransaction = false;

    private boolean globalRollbackOnParticipationFailure = true;

    private boolean failEarlyOnGlobalRollbackOnly = false;

    private boolean rollbackOnCommitFailure = false;


    @Override
    public final TransactionStatus getTransaction(TransactionDefinition definition) {
        TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
        /**
         * 获取事务对象
         */
        Object transaction = doGetTransaction();

        /**
         * 存在事务，检查事务传播行为
         * 子类实现，各个具体实现不一
         */
        if (isExistingTransaction(transaction)) {
            return handleExistingTransaction(def, transaction);
        }

        if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
            throw new RuntimeException("Invalid transaction timeout：" + def.getTimeout());
        }

        /**
         * 如果当前存在事务，则加入当前事务；如果当前没有事务，则抛出异常
         */
        if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
            throw new RuntimeException(
                    "No existing transaction found for transaction marked with propagation 'mandatory'");
        } else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
                def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
                def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
            /**
             * 暂停事务
             */
            SuspendedResourcesHolder suspendedResources = suspend(null);
            try {
                return startTransaction(def, transaction, suspendedResources);
            } catch (RuntimeException | Error ex) {
                /**
                 * 恢复事务
                 */
                resume(null, suspendedResources);
                throw ex;
            }
        } else {
            boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
            return prepareTransactionStatus(def, null, true, newSynchronization, null);
        }
    }

    protected int determineTimeout(TransactionDefinition definition) {
        if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) {
            return definition.getTimeout();
        }
        return getDefaultTimeout();
    }

    @Override
    public final void commit(TransactionStatus status) {
        if (status.isCompleted()) {
            throw new RuntimeException(
                    "Transaction is already completed - do not call commit or rollback more than once per transaction");
        }

        DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
        if (defStatus.isLocalRollbackOnly()) {
            processRollback(defStatus, false);
            return;
        }

        if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
            processRollback(defStatus, true);
            return;
        }

        processCommit(defStatus);
    }

    @Override
    public final void rollback(TransactionStatus status) {
        /**
         * 已完成，直接抛出异常
         */
        if (status.isCompleted()) {
            throw new RuntimeException(
                    "Transaction is already completed - do not call commit or rollback more than once per transaction");
        }

        DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
        processRollback(defStatus, false);
    }

    private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
        try {
            boolean unexpectedRollback = unexpected;

            try {
                //触发完成前回调
                triggerBeforeCompletion(status);
                /**
                 * 存在savepoint，回滚到保存点
                 */
                if (status.hasSavepoint()) {
                    status.rollbackToHeldSavepoint();
                } else if (status.isNewTransaction()) {//新事务直接提交
                    doRollback(status);
                } else {
                    //全局事务且存在保存点
                    if (status.hasTransaction()) {
                        if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
                            doSetRollbackOnly(status);
                        }
                    }
                    if (!isFailEarlyOnGlobalRollbackOnly()) {
                        unexpectedRollback = false;
                    }
                }
            } catch (RuntimeException | Error ex) {
                triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
                throw ex;
            }

            triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);

            if (unexpectedRollback) {
                throw new RuntimeException(
                        "Transaction rolled back because it has been marked as rollback-only");
            }
        } finally {
            cleanupAfterCompletion(status);
        }
    }

    private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction,
                                               SuspendedResourcesHolder suspendedResources) {

        boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
        DefaultTransactionStatus status = newTransactionStatus(
                definition, transaction, true, newSynchronization, suspendedResources);
        doBegin(transaction, definition);
        prepareSynchronization(status, definition);
        return status;
    }

    private TransactionStatus handleExistingTransaction(
            TransactionDefinition definition, Object transaction) {

        /**
         * 没有事务执行，则直接报错
         */
        if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
            throw new RuntimeException(
                    "Existing transaction found for transaction marked with propagation 'never'");
        }

        /**
         *	如果当前存在事务，则挂起当前事务，方法以非事务性方式执行。如果没有事务，方法也是非事务性的
         */
        if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
            Object suspendedResources = suspend(transaction);
            boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
            return prepareTransactionStatus(
                    definition, null, false, newSynchronization, suspendedResources);
        }
        /**
         * 始终启动一个新的事务。如果已经存在事务，当前事务将被挂起，直到新的事务完成。
         */
        if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
            SuspendedResourcesHolder suspendedResources = suspend(transaction);
            try {
                return startTransaction(definition, transaction, suspendedResources);
            } catch (RuntimeException | Error beginEx) {
                resumeAfterBeginException(transaction, suspendedResources, beginEx);
                throw beginEx;
            }
        }
        /**
         * 如果当前存在事务，创建一个嵌套事务；如果没有事务，则行为与 PROPAGATION_REQUIRED 相同
         */
        if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
            if (!isNestedTransactionAllowed()) {
                throw new RuntimeException(
                        "Transaction manager does not allow nested transactions by default - " +
                                "specify 'nestedTransactionAllowed' property with value 'true'");
            }
            if (useSavepointForNestedTransaction()) {
                DefaultTransactionStatus status =
                        prepareTransactionStatus(definition, transaction, false, false, null);
                status.createAndHoldSavepoint();
                return status;
            } else {
                return startTransaction(definition, transaction, null);
            }
        }

        if (isValidateExistingTransaction()) {
            if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
                Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
                if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {
                    Constants isoConstants = DefaultTransactionDefinition.constants;
                    throw new RuntimeException("Participating transaction with definition [" +
                            definition + "] specifies isolation level which is incompatible with existing transaction: " +
                            (currentIsolationLevel != null ?
                                    isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) :
                                    "(unknown)"));
                }
            }
            if (!definition.isReadOnly()) {
                if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
                    throw new RuntimeException("Participating transaction with definition [" +
                            definition + "] is not marked as read-only but existing transaction is");
                }
            }
        }
        boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
        return prepareTransactionStatus(definition, transaction, false, newSynchronization, null);
    }

    private void processCommit(DefaultTransactionStatus status) {
        try {

            try {
                boolean unexpectedRollback = false;
                /**
                 * 子类拓展
                 */
                prepareForCommit(status);
                /**
                 * 触发相应回调
                 */
                triggerBeforeCommit(status);
                triggerBeforeCompletion(status);

                /**
                 * 存在保存点
                 */
                if (status.hasSavepoint()) {
                    /**
                     * 全局回滚状态
                     */
                    unexpectedRollback = status.isGlobalRollbackOnly();
                    /**
                     * 释放保存点
                     */
                    status.releaseHeldSavepoint();
                } else if (status.isNewTransaction()) {
                    /**
                     * 新事务直接提交
                     */
                    unexpectedRollback = status.isGlobalRollbackOnly();
                    doCommit(status);
                } else if (isFailEarlyOnGlobalRollbackOnly()) {
                    /**
                     * 全局事务出现异常
                     */
                    unexpectedRollback = status.isGlobalRollbackOnly();
                }

                /**
                 * 全局回滚状态则直接回滚
                 */
                if (unexpectedRollback) {
                    throw new RuntimeException(
                            "Transaction silently rolled back because it has been marked as rollback-only");
                }
            } catch (RuntimeException ex) {
                /**
                 * 触发事务完成回调
                 */
                triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
                throw ex;
            }

            try {
                /**
                 * 触发提交完成回调
                 */
                triggerAfterCommit(status);
            } finally {
                /**
                 * 触发事务完成回调
                 */
                triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
            }

        } finally {
            /**
             * 清理当前事务信息
             */
            cleanupAfterCompletion(status);
        }
    }

    private void doRollbackOnCommitException(DefaultTransactionStatus status, Throwable ex) {
        try {
            if (status.isNewTransaction()) {
                doRollback(status);
            } else if (status.hasTransaction() && isGlobalRollbackOnParticipationFailure()) {
                doSetRollbackOnly(status);
            }
        } catch (RuntimeException | Error rbex) {
            triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
            throw rbex;
        }
        triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
    }

    private void resumeAfterBeginException(
            Object transaction, SuspendedResourcesHolder suspendedResources, Throwable beginEx) {

        try {
            resume(transaction, suspendedResources);
        } catch (RuntimeException | Error resumeEx) {
            throw resumeEx;
        }
    }

    /**
     * 恢复事务
     *
     * @param transaction
     * @param resourcesHolder
     */
    protected final void resume(Object transaction, SuspendedResourcesHolder resourcesHolder) {

        if (resourcesHolder != null) {
            Object suspendedResources = resourcesHolder.suspendedResources;
            if (suspendedResources != null) {
                /**
                 * 子类实现
                 */
                doResume(transaction, suspendedResources);
            }
            List<TransactionSynchronization> suspendedSynchronizations = resourcesHolder.suspendedSynchronizations;
            /**
             * 恢复暂时时的事务
             */
            if (suspendedSynchronizations != null) {
                TransactionSynchronizationManager.setActualTransactionActive(resourcesHolder.wasActive);
                TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(resourcesHolder.isolationLevel);
                TransactionSynchronizationManager.setCurrentTransactionReadOnly(resourcesHolder.readOnly);
                TransactionSynchronizationManager.setCurrentTransactionName(resourcesHolder.name);
                /**
                 * 触发恢复回调
                 */
                doResumeSynchronization(suspendedSynchronizations);
            }
        }
    }

    protected final DefaultTransactionStatus prepareTransactionStatus(
            TransactionDefinition definition, Object transaction, boolean newTransaction,
            boolean newSynchronization, Object suspendedResources) {

        DefaultTransactionStatus status = newTransactionStatus(
                definition, transaction, newTransaction, newSynchronization, suspendedResources);
        prepareSynchronization(status, definition);
        return status;
    }

    protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
        /**
         * 是否创建新事务
         */
        if (status.isNewSynchronization()) {
            TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());
            TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(
                    definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ?
                            definition.getIsolationLevel() : null);
            TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());
            TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());
            TransactionSynchronizationManager.initSynchronization();
        }
    }

    protected void registerAfterCompletionWithExistingTransaction(
            Object transaction, List<TransactionSynchronization> synchronizations) {
        invokeAfterCompletion(synchronizations, TransactionSynchronization.STATUS_UNKNOWN);
    }

    private void triggerAfterCompletion(DefaultTransactionStatus status, int completionStatus) {
        if (status.isNewSynchronization()) {
            List<TransactionSynchronization> synchronizations = TransactionSynchronizationManager.getSynchronizations();
            TransactionSynchronizationManager.clearSynchronization();
            if (!status.hasTransaction() || status.isNewTransaction()) {
                invokeAfterCompletion(synchronizations, completionStatus);
            } else if (!synchronizations.isEmpty()) {
                registerAfterCompletionWithExistingTransaction(status.getTransaction(), synchronizations);
            }
        }
    }


    private void cleanupAfterCompletion(DefaultTransactionStatus status) {
        status.setCompleted();
        if (status.isNewSynchronization()) {
            TransactionSynchronizationManager.clear();
        }
        if (status.isNewTransaction()) {
            doCleanupAfterCompletion(status.getTransaction());
        }
        if (status.getSuspendedResources() != null) {
            Object transaction = (status.hasTransaction() ? status.getTransaction() : null);
            resume(transaction, (SuspendedResourcesHolder) status.getSuspendedResources());
        }
    }

    protected abstract void doCommit(DefaultTransactionStatus status);

    protected void prepareForCommit(DefaultTransactionStatus status) {
    }

    protected abstract void doRollback(DefaultTransactionStatus status);

    protected void doCleanupAfterCompletion(Object transaction) {
    }

    protected void doSetRollbackOnly(DefaultTransactionStatus status) {
        throw new RuntimeException(
                "Participating in existing transactions is not supported - when 'isExistingTransaction' " +
                        "returns true, appropriate 'doSetRollbackOnly' behavior must be provided");
    }

    protected boolean shouldCommitOnGlobalRollbackOnly() {
        return false;
    }

    /**
     * 创建事务状态
     *
     * @param definition
     * @param transaction
     * @param newTransaction
     * @param newSynchronization
     * @param suspendedResources
     * @return
     */
    protected DefaultTransactionStatus newTransactionStatus(
            TransactionDefinition definition, Object transaction, boolean newTransaction,
            boolean newSynchronization, Object suspendedResources) {

        boolean actualNewSynchronization = newSynchronization &&
                !TransactionSynchronizationManager.isSynchronizationActive();
        return new DefaultTransactionStatus(
                transaction, newTransaction, actualNewSynchronization,
                definition.isReadOnly(), suspendedResources);
    }

    /**
     * 暂停事务
     *
     * @param transaction
     * @return
     */
    protected final SuspendedResourcesHolder suspend(Object transaction) {
        /**
         * 是否活跃
         */
        if (TransactionSynchronizationManager.isSynchronizationActive()) {
            //触发所有暂定回调
            List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();
            try {
                Object suspendedResources = null;
                if (transaction != null) {
                    //子类实现
                    suspendedResources = doSuspend(transaction);
                }
                /**
                 * 清理ThreadLocal
                 */
                String name = TransactionSynchronizationManager.getCurrentTransactionName();
                TransactionSynchronizationManager.setCurrentTransactionName(null);
                boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
                TransactionSynchronizationManager.setCurrentTransactionReadOnly(false);
                Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
                TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null);
                boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive();
                TransactionSynchronizationManager.setActualTransactionActive(false);
                return new SuspendedResourcesHolder(
                        suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive);
            } catch (RuntimeException | Error ex) {
                doResumeSynchronization(suspendedSynchronizations);
                throw ex;
            }
        } else if (transaction != null) {
            Object suspendedResources = doSuspend(transaction);
            return new SuspendedResourcesHolder(suspendedResources);
        } else {
            return null;
        }
    }

    protected boolean useSavepointForNestedTransaction() {
        return true;
    }

    /**
     * 执行激活回调
     *
     * @param suspendedSynchronizations
     */
    private void doResumeSynchronization(List<TransactionSynchronization> suspendedSynchronizations) {
        TransactionSynchronizationManager.initSynchronization();
        for (TransactionSynchronization synchronization : suspendedSynchronizations) {
            synchronization.resume();
            //重新注册
            TransactionSynchronizationManager.registerSynchronization(synchronization);
        }
    }

    /**
     * 执行暂停回调
     *
     * @return
     */
    private List<TransactionSynchronization> doSuspendSynchronization() {
        List<TransactionSynchronization> suspendedSynchronizations =
                TransactionSynchronizationManager.getSynchronizations();
        for (TransactionSynchronization synchronization : suspendedSynchronizations) {
            //执行回调
            synchronization.suspend();
        }
        //从线程中解绑回调
        TransactionSynchronizationManager.clearSynchronization();
        return suspendedSynchronizations;
    }

    protected Object doSuspend(Object transaction) {
        throw new RuntimeException(
                "Transaction manager [" + getClass().getName() + "] does not support transaction suspension");
    }

    protected abstract void doBegin(Object transaction, TransactionDefinition definition);

    protected void doResume(Object transaction, Object suspendedResources) {
        throw new RuntimeException(
                "Transaction manager [" + getClass().getName() + "] does not support transaction suspension");
    }

    /**
     * 是否存在事务
     *
     * @param transaction
     * @return
     */
    protected boolean isExistingTransaction(Object transaction) {
        return false;
    }

    /**
     * 获取事务信息
     *
     * @return
     */
    protected abstract Object doGetTransaction();

    public final void setTransactionSynchronizationName(String constantName) {
        setTransactionSynchronization(constants.asNumber(constantName).intValue());
    }


    public final void setDefaultTimeout(int defaultTimeout) {
        if (defaultTimeout < TransactionDefinition.TIMEOUT_DEFAULT) {
            throw new RuntimeException("Invalid default timeout：" + defaultTimeout);
        }
        this.defaultTimeout = defaultTimeout;
    }


    /**
     * 悬挂事务信息保存
     */
    protected static final class SuspendedResourcesHolder {

        /**
         * 事务信息
         */
        private final Object suspendedResources;
        /**
         * 同步器
         */
        private List<TransactionSynchronization> suspendedSynchronizations;

        private String name;

        private boolean readOnly;

        /**
         * 隔离级别
         */
        private Integer isolationLevel;

        /**
         * 是否活跃
         */
        private boolean wasActive;

        private SuspendedResourcesHolder(Object suspendedResources) {
            this.suspendedResources = suspendedResources;
        }

        private SuspendedResourcesHolder(
                Object suspendedResources, List<TransactionSynchronization> suspendedSynchronizations,
                String name, boolean readOnly, Integer isolationLevel, boolean wasActive) {

            this.suspendedResources = suspendedResources;
            this.suspendedSynchronizations = suspendedSynchronizations;
            this.name = name;
            this.readOnly = readOnly;
            this.isolationLevel = isolationLevel;
            this.wasActive = wasActive;
        }
    }

}
